package com.xunchi.haichuan.utility;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;

/**
 * 作者：kongxb<br>
 * 模块名称：工具模块<br>
 * 功能描述：字节码工具<br>
 * 维护记录：2009.11.27, kongxb 创建该类
 */
public class ByteArrayEncoder {
	/**
	 * The default encoding.
	 */
	private final static String DEFAULT_ENCODING = "UTF-8";

	/**
	 * The current encoding. Can not be changed during the object 'life'.
	 */
	private String encoding = DEFAULT_ENCODING;

	/**
	 * Indicate whether the encoder is closed or not. If closed, it's impossible to append new data to it. If not
	 * closed, it's impossible to get the encoded length or the encoded byte array.<BR>
	 * <B>Note:</B> a closed byte array can not be re-opened.
	 */
	private boolean closed = false;

	/**
	 * The actual array, which will collect the encoded bytes.
	 */
	private ByteArrayOutputStream baos = new ByteArrayOutputStream();

	/**
	 * The byte array length. Calculated when the ByteArrayOutput is closed.
	 */
	private int encodedLength = -1;

	/**
	 * The encoded byte array. Calculated when the ByteArrayOutput is closed.
	 */
	private byte[] encodedByteArray = null;

	/**
	 * The writer, that will encode the input parameters to {@link #baos}.
	 */
	private Writer writer;

	// ///////////////////////////////////////////////////////////////////////
	// //////////////// CONSTRUCTORS /////////////////////////////////////////
	// ///////////////////////////////////////////////////////////////////////

	/**
	 * Create an encoder, using the DEFAULT_ENCODING encoding.
	 * 
	 * @throws Exception
	 */
	public ByteArrayEncoder() throws Exception {
		init(DEFAULT_ENCODING);
	}

	/**
	 * Create an encoder, and specifies the encoding to use.
	 * 
	 * @param encoding
	 * @throws Exception
	 */
	public ByteArrayEncoder(String encoding) throws Exception {
		init(encoding);
	}

	// ///////////////////////////////////////////////////////////////////////
	// //////////////// Public methods ///////////////////////////////////////
	// ///////////////////////////////////////////////////////////////////////

	/**
	 * @throws Exception
	 */
	synchronized public void close() throws Exception {
		if (isClosed()) {
			throw new Exception("Trying to get length of a on non-closed ByteArrayEncoded");
		}
		try {
			this.writer.close();
		} catch (IOException e) {
			throw new Exception(e);
		}
		this.encodedByteArray = this.baos.toByteArray();
		this.closed = true;
	}

	/** {@inheritDoc} */
	public ByteArrayEncoder append(String str) throws Exception {
		try {
			this.writer.append(str);
		} catch (IOException e) {
			throw new Exception(e);
		}
		// Returning the encoder allows calls like:
		// bae.append("qdqd").append("qsldqd"); (like StringBuffer)
		return this;
	}

	public void println(String str) throws Exception {
		append(str).append("\r\n");
	}

	/**
	 * @return value of the DEFAULT_ENCODING constant.
	 */
	public static String getDefaultEncoding() {
		return DEFAULT_ENCODING;
	}

	/** {@inheritDoc} */
	public boolean isClosed() {
		return this.closed;
	}

	/** {@inheritDoc} */
	public String getEncoding() {
		return this.encoding;
	}

	/** {@inheritDoc} */
	public int getEncodedLength() throws Exception {
		if (!isClosed()) {
			throw new Exception("Trying to get length of a on non-closed ByteArrayEncoded");
		}
		return this.encodedLength;
	}

	/** {@inheritDoc} */
	public byte[] getEncodedByteArray() throws Exception {
		if (!isClosed()) {
			throw new Exception("Trying to get length of a on non-closed ByteArrayEncoded");
		}
		return this.encodedByteArray;
	}

	/** {@inheritDoc} */
	public String getString() throws Exception {
		if (!isClosed()) {
			throw new Exception("Trying to get length of a on non-closed ByteArrayEncoded");
		}
		try {
			return new String(this.encodedByteArray, encoding);
		} catch (UnsupportedEncodingException e) {
			throw new Exception(e);
		}
	}

	// ///////////////////////////////////////////////////////////////////////
	// //////////////// Private methods //////////////////////////////////////
	// ///////////////////////////////////////////////////////////////////////

	/**
	 * Initialization.
	 * 
	 * @throws Exception
	 */
	public void init(String encoding) throws Exception {
		this.encoding = encoding;

		try {
			this.writer = new OutputStreamWriter(this.baos, encoding);
		} catch (UnsupportedEncodingException e) {
			throw new Exception(e);
		}
	}

}
